﻿using System;
using System.Reflection;
using Itau.IM.Common.Constants;
using Itau.IM.Common.Interface;
using Itau.IM.Common.Logging;
using Itau.IM.Common.Utility;
using Spring.Aop;

namespace Itau.IM.Common.Aop
{
    /// <summary>
    /// Classe de Trace por Orientação a Aspectos, antes da execução
    /// </summary>
    public class AspectTraceBeforeMethod : IMethodBeforeAdvice
    {
        #region Atributos

        private ILog _log;
        private DateTime _beginning;
        private bool _singleCall;
        private bool _aspectTraceEnabled;

        #endregion Atributos

        #region Propriedades

        public ILog Log
        {
            get { return this._log; }
        }

        public DateTime Beginning
        {
            get { return this._beginning; }
        }

        public bool SingleCall
        {
            get { return this._singleCall; }
        }

        #endregion Propriedades

        #region Métodos

        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="log_">Objeto de log</param>
        public AspectTraceBeforeMethod(ILog log_)
        {
            this._aspectTraceEnabled = Convert.ToBoolean(Configuration.GetSetting(AopConstants.AOP_TRACE_ENABLED));

            this._singleCall = false;

            this._log = log_;
        }

        /// <summary>
        /// Construtor para execução única (ex: WCF)
        /// </summary>
        /// <param name="routineName_">Nome da rotina</param>
        public AspectTraceBeforeMethod(string routineName_)
        {
            this._aspectTraceEnabled = Convert.ToBoolean(Configuration.GetSetting(AopConstants.AOP_TRACE_ENABLED));

            this._singleCall = true;

            this._log = new LogFile(routineName_);
            this._log.BeginLog();
        }

        /// <summary>
        /// Método responsável pelo log do início da execução do método
        /// </summary>
        /// <param name="method">Método</param>
        /// <param name="args">Parâmetros</param>
        /// <param name="target">Classe</param>
        public void Before(MethodInfo method, object[] args, object target)
        {
            if (this._aspectTraceEnabled)
            {
                this._beginning = DateTime.Now;

                this._log.LogInfo("Iniciando execução do método '{0}.{1}'", target.ToString(), method.Name);

                for (int i = 0; i < method.GetParameters().Length; i++)
                {
                    ParameterInfo parameter = method.GetParameters()[i];

                    if (parameter.ParameterType.IsPrimitive)
                    {
                        this._log.LogInfo("Parâmetro '{0}' com valor: {1}", parameter.Name, args[i].ToString());
                    }
                    else
                    {
                        this._log.LogInfo("Parâmetro '{0}' com valor: {1}{2}", parameter.Name, Environment.NewLine, new Serialization(parameter.ParameterType).Serialize(args[i]));
                    }
                }
            }
        }

        #endregion Métodos
    }

    /// <summary>
    /// Classe de Trace por Orientação a Aspectos, após a execução
    /// </summary>
    public class AspectTraceAfterMethod : IAfterReturningAdvice
    {
        #region Atributos

        private ILog _log;
        private DateTime _beginning;
        private bool _singleCall;
        private bool _aspectTraceEnabled;

        #endregion Atributos

        #region Métodos

        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="beforeAdvice_">Instância da classe AspectTraceBeforeMethod</param>
        public AspectTraceAfterMethod(AspectTraceBeforeMethod beforeAdvice_)
        {
            this._aspectTraceEnabled = Convert.ToBoolean(Configuration.GetSetting(AopConstants.AOP_TRACE_ENABLED));

            this._log = beforeAdvice_.Log;
            this._singleCall = beforeAdvice_.SingleCall;
            this._beginning = beforeAdvice_.Beginning;
        }

        /// <summary>
        /// Método responsável pelo log do término da execução do método
        /// </summary>
        /// <param name="returnValue">Retorno</param>
        /// <param name="method">Método</param>
        /// <param name="args">Parâmetros</param>
        /// <param name="target">Classe</param>
        public void AfterReturning(object returnValue, MethodInfo method, object[] args, object target)
        {
            if (this._aspectTraceEnabled)
            {
                TimeSpan executionTime = DateTime.Now - this._beginning;

                this._log.LogInfo("Concluída a execução do método '{0}.{1}' em {2} milisegundos", target.ToString(), method.Name, executionTime.Milliseconds);

                if (method.ReturnType.IsPrimitive)
                {
                    this._log.LogInfo("Resultado do método: {0}", returnValue.ToString());
                }
                else
                {
                    this._log.LogInfo("Resultado do método: {0}{1}", Environment.NewLine, new Serialization(method.ReturnType).Serialize(returnValue));
                }

                if (this._singleCall)
                {
                    this._log.FinishLog();
                }
            }
        }

        #endregion Métodos
    }
}